package cn.edu.gdu.quizpractice.common.config;

import cn.edu.gdu.quizpractice.service.impl.ManagerUserDetails;
import cn.edu.gdu.quizpractice.common.utils.JwtUtils;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.util.StringUtils;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import java.io.IOException;
import java.util.List;
/**
 * @Author: Aubery
 * @Date: 2025-01-27-15:00
 * @Description:
 */

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
@EnableWebMvc
public class SecurityConfig {

    @Autowired
    private ManagerUserDetails managerUserDetailsService;
    @Autowired
    private JwtUtils jwtUtils;

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .cors(cors -> cors.configurationSource(corsConfigurationSource()))
            .csrf(csrf -> csrf.disable())
            .sessionManagement(session -> session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            )
            .authorizeHttpRequests(auth -> auth
                // mini路径直接放行
                .requestMatchers("/mini/**").permitAll()
                // web管理模块特定路径设置
                .requestMatchers("/web/managers/login").permitAll()
                .requestMatchers("/web/managers/profile").authenticated()
                // 其他web路径需要认证
                .anyRequest().authenticated()
            )
            .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }

    @Bean
    public OncePerRequestFilter jwtAuthenticationFilter() {
        // Define paths that should bypass JWT authentication attempt in this filter
        // These should generally match your permitAll() paths
        final List<String> permitAllPaths = List.of(
            "/mini/**",
            "/web/managers/login",
            "/error"    // Usually good to permit error pages
            // Add other permitAll paths from your HttpSecurity config if necessary
        );

        return new OncePerRequestFilter() {
            @Override
            protected void doFilterInternal(HttpServletRequest request,
                                           HttpServletResponse response,
                                           FilterChain filterChain) throws ServletException, IOException {
                String requestPath = request.getServletPath();

                // Check if the current request path matches any permitAll path
                boolean requiresAuthentication = permitAllPaths.stream()
                        .noneMatch(path -> new AntPathRequestMatcher(path).matches(request));

                String jwt = null;
                String username = null;

                if (requiresAuthentication) {
                    // Only process token if authentication is required for the path
                    String authHeader = request.getHeader("Authorization");
                    if (StringUtils.hasText(authHeader) && authHeader.startsWith("Bearer ")) {
                        jwt = authHeader.substring(7);
                        try {
                            // Attempt to extract username (assuming it's a manager token for now)
                            username = jwtUtils.getUsernameFromToken(jwt);
                        } catch (IllegalArgumentException e) {
                            System.err.println("Token解析失败: " + e.getMessage());
                        } catch (Exception e) {
                            System.err.println("Error extracting username from token: " + e.getMessage());
                        }
                    } else {
                         System.out.println("No Bearer token found for protected path: " + requestPath);
                    }

                    // If username extracted and no authentication context exists yet
                    if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                        try {
                             // Load details using MANAGER service (as this filter is currently set up)
                            UserDetails userDetails = managerUserDetailsService.loadUserByUsername(username);
                            if (jwtUtils.validateToken(jwt, userDetails)) {
                                SecurityContextHolder.getContext().setAuthentication(
                                    new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities())
                                );
                                System.out.println("Authenticated manager: " + username + " for path: " + requestPath);
                            } else {
                                 System.out.println("Invalid JWT token for username: " + username);
                            }
                        } catch (org.springframework.security.core.userdetails.UsernameNotFoundException e) {
                             System.err.println("Manager not found for username: " + username);
                        } catch (Exception e) {
                            System.err.println("Error during authentication process for " + username + ": " + e.getMessage());
                        }
                    }
                } else {
                    System.out.println("Permitting request to public path: " + requestPath);
                }

                filterChain.doFilter(request, response);
            }
        };
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();//解决跨域问题
        configuration.setAllowedOrigins(List.of("*")); // 允许所有来源
        configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE")); // 允许的 HTTP 方法
        configuration.setAllowedHeaders(List.of("*")); // 允许所有请求头
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }


}
